#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
#include <opencv2/opencv.hpp>
#include "opencv2/core/core.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include "helpers.hpp"
#include "eye_detector.hpp"
#include "mouth_detector.hpp"



using namespace std;
using namespace cv;

#define PORT 3456
#define FRAME_WIDTH 640
#define FRAME_HEIGHT 480

void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main()
{
    int sockfd, newsockfd, portno, n, imgSize, bytes = 0, IM_HEIGHT, IM_WIDTH;
    socklen_t clilen;
    char buffer[256];
    struct sockaddr_in serv_addr, cli_addr;
    cv::CascadeClassifier face_cascade;
    face_cascade.load("/home/hx/opencv-4.x/data/haarcascades/haarcascade_frontalface_default.xml");
    cv::CascadeClassifier eye_cascade;
    eye_cascade.load("/home/hx/opencv-4.x/data/haarcascades/haarcascade_eye.xml");
    cv::CascadeClassifier smile_cascade;
    smile_cascade.load("/home/hx/opencv-4.x/data/haarcascades/haarcascade_smile.xml");

    Mat img;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) error("ERROR opening socket");

    bzero((char *)&serv_addr, sizeof(serv_addr));
    portno = PORT;

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);

    if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
        error("ERROR on binding");

    listen(sockfd, 5);
    clilen = sizeof(cli_addr);

    newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
    if (newsockfd < 0) error("ERROR on accept");

    bool running = true;

    while (running)
    {
        IM_HEIGHT = FRAME_HEIGHT;
        IM_WIDTH = FRAME_WIDTH;
        img = Mat::zeros(FRAME_HEIGHT, FRAME_WIDTH, CV_8UC3);

        imgSize = img.total() * img.elemSize();
        uchar sockData[imgSize];

        for (int i = 0; i < imgSize; i += bytes)
            if ((bytes = recv(newsockfd, sockData + i, imgSize - i, 0)) == -1)
                error("recv failed");

        int ptr = 0;

        for (int i = 0; i < img.rows; ++i)
            for (int j = 0; j < img.cols; ++j)
            {
                img.at<Vec3b>(i, j) = Vec3b(sockData[ptr + 0], sockData[ptr + 1], sockData[ptr + 2]);
                ptr = ptr + 3;
            }

        cv::Mat grayFrame;
        cv::cvtColor(img, grayFrame, cv::COLOR_BGR2GRAY);

        std::vector<cv::Rect> faces;
        face_cascade.detectMultiScale(grayFrame, faces, 1.1, 6, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30));

        for (const auto &face : faces)
        {
            cv::rectangle(img, face, cv::Scalar(0, 255, 0), 2);
            cv::Mat faceROI = grayFrame(face);
            detectAndDrawEyes(img, faceROI, face, eye_cascade);
            detectAndDrawMouth(img, faceROI, face, smile_cascade);
        }

        namedWindow("Server", CV_MINOR_VERSION);
        imshow("Server", img);
        char key = waitKey(5);
        if (key == 27) running = false;
    }

    close(newsockfd);
    close(sockfd);

    return 0;
}
